// Copyright (c) Evolus Solutions. All rights reserved.
// License: GPL/MPL
// $Id$

export default class ShapeDef {
    public id: string;
    public displayName: string;
    public contentNode: string;
    public propertyGroups: PropertyGroup[];
    public behaviors: Behavior[];
    public actions: ShapeAction[];
    public propertyMap: {};
    public behaviorMap: {};
    public actionMap: {};
    public collection?: any;
    public connection?: any;

    constructor() {
        this.id = null;
        this.displayName = null;

        this.contentNode = null;
        this.propertyGroups = [];
        this.behaviors = [];
        this.actions = [];

        this.propertyMap = {};
        this.behaviorMap = {};
        this.actionMap = {};
    }

    toString() {
        return "[ShapeDef: " + this.id + "]";
    };

    getProperty(name) {
        return this.propertyMap[name];
    };

    removeProperty(name) {
        var found = false;
        for (var group of this.propertyGroups) {
            for (var i = 0; i < group.properties.length; i++) {
                var property = group.properties[i];
                if (property.name == name) {
                    group.properties.splice(i, 1);
                    found = true;
                    break;
                }
            }

            if (found) break;
        }

        if (found) {
            delete this.propertyMap[name];
        }
    };

    removeAction(id) {
        for (var i = 0; i < this.actions.length; i++) {
            var action = this.actions[i];
            if (action.id == id) {
                this.actions.splice(i, 1);
                delete this.actionMap[id];
                break;
            }
        }
    };

    isPropertyAffectedBy(target, source, checkedProperties) {
        if (target == source) return true;

        if (checkedProperties && checkedProperties[target]) return false;

        var tp = this.propertyMap[target];
        if (!tp) return false;

        var sp = this.propertyMap[source];
        if (!sp) return false;

        if (tp.relatedProperties[source]) return true;

        var props = checkedProperties ? checkedProperties : {};
        props[target] = true;
        for (let name in tp.relatedProperties) {
            if (this.isPropertyAffectedBy(name, source, props)) return true;
        }

        return false;
    };
}

class PropertyGroup {
    public name: string;
    public properties: Property[];

    constructor() {
        this.name = null;
        this.properties = [];
    }

    toString() {
        return "[PropertyGroup: " + this.name + "]";
    };

    clone() {
        var group = new PropertyGroup();
        group.name = this.name;
        for (var prop of this.properties) {
            group.properties.push(prop.clone());
        }

        return group;
    };
}
class Property {
    public name: string;
    public displayName: string;
    public type: string;
    public initialValue: string;
    public relatedTargets: {};
    public meta: { };

    public relatedProperties: {
        [propName: string]: any,
    };

    public initialValueExpression: string;

    constructor() {
        this.name = null;
        this.displayName = null;
        this.type = null;
        this.initialValue = null;

        this.relatedTargets = {};
        this.meta = {};
    }

    toString() {
        return "[Property: " + this.name + "]";
    };

    clone() {
        var property = new Property();
        property.name = this.name;
        property.displayName = this.displayName;
        property.type = this.type;
        property.initialValue = this.initialValue ? this.type.fromString(this.initialValue.toString()) : null;
        property.initialValueExpression = this.initialValueExpression;

        for (var name in this.relatedTargets) {
            property.relatedTargets[name] = this.relatedTargets[name];
        }

        property.relatedProperties = {};
        for (var name in this.relatedProperties) {
            property.relatedProperties[name] = this.relatedProperties[name];
        }

        for (var name in this.meta) {
            property.meta[name] = this.meta[name];
        }

        return property;
    };

    isSimilarTo(property) {
        return this.name == property.name &&
            this.type == property.type;
    };
}

class Behavior {
    public target: string;
    public items: BehaviorItem[];
    constructor() {
        this.target = null;
        this.items = [];
    }

    toString() {
        return "[Behavior: for " + this.target + "]";
    };
}

class BehaviorItem {
    handler: string;
    args: BehaviorItemArg[];
    constructor() {
        this.handler = null;
        this.args = [];
    }

    toString() {
        return "[BehaviorItem: " + this.handler + "]";
    };

}

class BehaviorItemArg {
    public literal: string;
    public type: string;
    constructor(literal, shapeDef: ShapeDef, currentTarget, type?) {
        this.literal = literal;
        this.type = type ? type : null;

        if (!this.type) {
            //preprocessing expression literal
            this.literal = this.literal.replace(/\$([a-z][a-z0-9]*)/gi, function (zero, one) {
                var property = shapeDef.getProperty(one);
                if (!property) {
                    throw Util.getMessage("invalid.property.reference", one) + " (" + shapeDef.id + ")";
                }
                property.relatedTargets[currentTarget] = true;
                return "properties." + one;
            });
        }
    }

    toString() {
        return "[BehaviorItemArg: " + this.literal + "]";
    };
}

class ShapeAction {
    public id: string;
    public displayName: string;
    public implFunction: string;
    constructor() {
        this.id = null;
        this.displayName = null;
        this.implFunction = null;
    }

    toString() {
        return "[ShapeAction: " + this.implFunction + "]";
    };
}

class Shortcut {
    public id: string;
    public name: string;
    public displayName: string;
    public shape: string;
    public propertyMap: {};
    constructor() {
        this.id = null;
        this.name = "";
        this.displayName = "";

        this.shape = null;
        this.propertyMap = {};
    }
}
